use std::error::Error;
use std::fs;
use std::fs::ReadDir;
use std::io::{self, ErrorKind};
use std::path::Path;
use tklog::{error, info};

use md5::{Digest, Md5};

#[derive(Debug)]
pub struct File {
    pub path: String,
    pub md5: String,
    pub content: String,
}

impl File {
    pub fn new(path: String, md5: String, content: String) -> Self {
        File { path, md5, content }
    }

    ///遍历文件夹下内容，并返回File列表
    pub fn scan_dir<P: AsRef<Path>>(dir_path: P) -> Result<Vec<File>, Box<dyn Error>> {
        //let parent_path = Path::new(dir_path);
        let mut file_vec: Vec<File> = vec![];

        let dir_iterator: ReadDir = fs::read_dir(dir_path)?;
        for entity in dir_iterator {
            let path = entity?.path();
            if path.is_dir() {
                file_vec.extend(File::scan_dir(&path)?);
            } else if path.is_file() {
                let suffix_option = path.extension();
                //只需要读取md后缀的文件
                if suffix_option.is_some() {
                    if suffix_option.unwrap().eq_ignore_ascii_case("md") {
                        let absolute_path = fs::canonicalize(path)?;
                        let path_string =
                            absolute_path.into_os_string().into_string().map_err(|e| {
                                std::io::Error::new(
                                    std::io::ErrorKind::Other,
                                    format!("Failed to convert path into string: {:?}", e),
                                )
                            })?;
                        let md5 = File::md5(&path_string.as_str())?;
                        let content = File::read(&path_string.as_str())?;
                        let file = File::new(path_string, md5, content);
                        file_vec.push(file);
                    }
                }
            }
        }
        Ok(file_vec)
    }

    ///获取文件md5摘要
    pub fn md5(file_path: &str) -> Result<String, Box<dyn Error>> {
        let content = File::read(file_path).unwrap();
        // create a Md5 hasher instance
        let mut hasher = Md5::new();
        hasher.update(content);
        let md5 = hasher.finalize();
        Ok(format!("{:x}", md5))
    }

    ///读取文件内容
    pub fn read(file_path: &str) -> Result<String, Box<dyn Error>> {
        let content = fs::read_to_string(file_path)?;
        Ok(content)
    }

    /// 判断文件夹或者文件是否存在
    pub fn is_exists(source_path: &str) -> std::io::Result<bool> {
        fs::exists(source_path)
    }

    pub fn get_suffix(path: &Path) -> Result<String, Box<dyn Error>> {}

    /// 如果文件夹不存在就创建，存在直接返回true
    pub fn create_dir(dir_path: &str) -> std::io::Result<bool> {
        info!("the parameter is {x}", &dir_path);
        // 尝试创建目录，如果成功或失败但因为已经存在，则返回 true
        if dir_path.is_empty() {
            error!("The parameter dir_path is empty!!!");
            return Err(std::io::Error::new(
                ErrorKind::InvalidInput,
                "Directory path cannot be empty",
            ));
        }
        // 如果路径已经存在，返回 true 并记录信息
        if File::is_exists(dir_path)? {
            info!("The path {} exists.", dir_path);
            return Ok(true);
        }
        // 尝试创建目录，如果成功或失败但因为已经存在，则返回 true
        match fs::create_dir_all(dir_path) {
            Ok(_) => {
                info!("Successfully created the directory at {}", dir_path);
                Ok(true)
            }
            Err(e) => {
                if e.kind() == ErrorKind::AlreadyExists {
                    info!("The path {} already exists.", dir_path);
                    Ok(true)
                } else {
                    error!("Failed to create directory at {}: {}", dir_path, e);
                    Err(e)
                }
            }
        }
    }

    pub fn create_file(path: &str, data: &[u8]) -> std::io::Result<()> {
        let mut buffer = BufWriter::new(File::create(path)?);
        buffer.write_all(data)?;
        buffer.flush()?;
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::io::Write;

    #[test]
    fn read() -> std::io::Result<()> {
        let mut f = std::fs::File::create_new("file_read.txt")?;
        write!(&mut f, "hello world")?;

        let content_result = File::read("./file_read.txt");
        match content_result {
            Ok(c) => {
                assert_eq!("hello world", c);
                fs::remove_file("file_read.txt")?;
            }
            Err(e) => {
                eprintln!("{:?}", e);
                fs::remove_file("file_read.txt")?;
            }
        }
        Ok(())
    }

    #[test]
    fn md5() -> std::io::Result<()> {
        let mut f = std::fs::File::create_new("file_read.txt")?;
        write!(&mut f, "hello world")?;

        let content_result = File::read("./file_read.txt");
        match content_result {
            Ok(c) => {
                assert_eq!("hello world", c);
            }
            Err(e) => {
                eprintln!("{:?}", e);
            }
        }
        let md5_result = File::md5("./file_read.txt");
        match md5_result {
            Ok(md5) => {
                assert_eq!(md5, "5eb63bbbe01eeed093cb22bb8f5acdc3");
            }
            Err(e) => {
                eprintln!("{:?}", e);
            }
        }
        fs::remove_file("file_read.txt")?;
        Ok(())
    }

    #[test]
    fn scan_dir() -> Result<(), Box<dyn Error>> {
        let dir_path = "/home/owen/文档/";
        let file_vec = File::scan_dir(dir_path)?;
        print!("{:?}", file_vec);
        Ok(())
    }

    #[test]
    fn is_exists() {
        let r = File::is_exists("./test/");
        assert_eq!(r.unwrap(), true);
        let r1 = File::is_exists("./test/hello/");
        assert_eq!(r1.unwrap(), false);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fs;
    use std::io::Result;

    #[test]
    fn test_create_dir_success() -> Result<()> {
        let test_dir = "./test_create_dir_success/";

        // 清理之前的测试残留
        let _ = fs::remove_dir_all(test_dir);

        // 尝试创建目录并检查结果
        let result = File::create_dir(test_dir);
        assert!(
            result.is_ok(),
            "Failed to create directory: {:?}",
            result.unwrap_err()
        );
        assert_eq!(result.unwrap(), true);

        // 确认目录已存在
        let exists_result = File::is_exists(test_dir);
        assert!(
            exists_result.is_ok(),
            "Failed to check existence of directory: {:?}",
            exists_result.unwrap_err()
        );
        assert_eq!(exists_result.unwrap(), true);

        // 再次尝试创建同一个目录
        let recreate_result = File::create_dir(test_dir);
        assert!(
            recreate_result.is_ok(),
            "Failed to recreate existing directory: {:?}",
            recreate_result.unwrap_err()
        );
        assert_eq!(recreate_result.unwrap(), true);

        // 清理测试产生的目录
        let _ = fs::remove_dir_all(test_dir);

        Ok(())
    }

    #[test]
    fn test_create_dir_empty_path() {
        // 测试空路径的情况
        let result = File::create_dir("");
        assert!(
            result.is_err(),
            "Expected an error for empty path, but got success"
        );
        if let Err(e) = result {
            assert_eq!(
                e.kind(),
                std::io::ErrorKind::InvalidInput,
                "Expected InvalidInput error kind"
            );
        }
    }

    #[test]
    fn test_create_dir_already_exists() -> Result<()> {
        let test_dir = "./test_create_dir_already_exists/";

        // 清理之前的测试残留
        let _ = fs::remove_dir_all(test_dir);

        // 创建目录
        let create_result = File::create_dir(test_dir);
        assert!(
            create_result.is_ok(),
            "Failed to create directory: {:?}",
            create_result.unwrap_err()
        );

        // 再次尝试创建同一个目录
        let recreate_result = File::create_dir(test_dir);
        assert!(
            recreate_result.is_ok(),
            "Failed to recreate existing directory: {:?}",
            recreate_result.unwrap_err()
        );

        // 清理测试产生的目录
        let _ = fs::remove_dir_all(test_dir);

        Ok(())
    }

    #[test]
    fn test_frominto() {}
}
